﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Oops.Xna.Framework
{
    /// <summary>
    /// An extension to the <see cref="Microsoft.Xna.Framework.Vector2" /> structure.
    /// </summary>
    public static class Vector2Extension
    {
		/// <summary>
		/// Creates a unit vector from the specified <see cref="Microsoft.Xna.Framework.Vector2" />.
		/// </summary>
		/// <param name="vector">The source <see cref="Microsoft.Xna.Framework.Vector2" />.</param>
		/// <returns>Returns the created unit vector.</returns>
		public static Vector2 SafeNormalize(Vector2 vector)
		{
			float lengthSquared = vector.X * vector.X + vector.Y * vector.Y;
			if (lengthSquared != 0.0f)
			{
#if FAST_INVSQRT
				float inverseLength = MathExtension.InvSqrt(lengthSquared);
#else
				float inverseLength = 1.0f / (float)Math.Sqrt(lengthSquared);
#endif
				vector.X *= inverseLength;
				vector.Y *= inverseLength;
			}

			return vector;
		}

		/// <summary>
		/// Modifies the specified <see cref="Microsoft.Xna.Framework.Vector2" /> into a unit vector.
		/// </summary>
		/// <param name="vector">The target <see cref="Microsoft.Xna.Framework.Vector2" />.</param>
		public static void SafeNormalize(ref Vector2 vector)
		{
			float lengthSquared = vector.X * vector.X + vector.Y * vector.Y;
			if (lengthSquared != 0.0f)
			{
#if FAST_INVSQRT
				float inverseLength = MathExtension.InvSqrt(lengthSquared);
#else
				float inverseLength = 1.0f / (float)Math.Sqrt(lengthSquared);
#endif
				vector.X *= inverseLength;
				vector.Y *= inverseLength;
			}
		}

        /// <summary>
        /// Determines if the specified vector has a magnitude of zero.
        /// </summary>
        /// <param name="vector">The source vector.</param>
		/// <returns><b>true</b> if the vector is zero length. Otherwise, <b>false</b>.</returns>
        public static bool IsZeroLength(this Vector2 vector)
        {
            return vector.LengthSquared() < (MathExtension.Epsilon * MathExtension.Epsilon);
        }

        /// <summary>
        /// Determines if the specified vector has a magnitude of zero.
        /// </summary>
		/// <param name="vector">The source vector.</param>
		/// <returns><b>true</b> if the vector is zero length. Otherwise, <b>false</b>.</returns>
        public static bool IsZeroLength(ref Vector2 vector)
        {
            return vector.LengthSquared() < (MathExtension.Epsilon * MathExtension.Epsilon);
        }

		/// <summary>
		/// Returns a vector that does not exceed the specified length.
		/// </summary>
		/// <param name="vector">The source vector.</param>
		/// <param name="length">The maximum length allowed for <paramref name="vector"/>.</param>
		/// <returns>A <see cref="Vector2"/> that has a length that does not exceed <paramref name="length"/>.</returns>
        public static Vector2 Truncate(Vector2 vector, float length)
        {
            if (vector.LengthSquared() > length * length)
            {
				Vector2.Normalize(ref vector, out vector);
                vector *= length;
            }

            return vector;
        }

		/// <summary>
		/// Returns a vector that does not exceed the specified length.
		/// </summary>
		/// <param name="vector">The source vector.</param>
		/// <param name="length">The maximum length allowed for <paramref name="vector"/>.</param>
		/// <param name="result">A <see cref="Vector2"/> that has a length that does not exceed <paramref name="length"/>.</param>
		public static void Truncate(ref Vector2 vector, float length, out Vector2 result)
		{
			if (vector.LengthSquared() > length * length)
			{
				Vector2.Normalize(ref vector, out result);
				result *= length;
			}
			else
			{
				result = vector;
			}
		}

		/// <summary>
		/// Converts the source <see cref="Vector2"/> to a <see cref="Vector3"/> with a zero y-component.
		/// </summary>
		/// <param name="source">The source vector.</param>
		/// <returns>
		/// A <see cref="Vector3"/> where <see cref="Vector3.X"/> equals <paramref name="source.X"/>
		/// and <see cref="Vector3.Y"/> equals zero and <see cref="Vector3.Z"/> equals 
		/// <paramref name="source.Z"/>.
		/// </returns>
		public static Vector3 ToVector3(this Vector2 source)
		{
			return new Vector3(source.X, 0.0f, source.Y);
		}

		/// <summary>
		/// Converts the source <see cref="Vector2"/> to a <see cref="Vector3"/> with a zero y-component.
		/// </summary>
		/// <param name="source">The source vector.</param>
		/// <returns>
		/// A <see cref="Vector3"/> where <see cref="Vector3.X"/> equals <paramref name="source.X"/>
		/// and <see cref="Vector3.Y"/> equals zero and <see cref="Vector3.Z"/> equals 
		/// <paramref name="source.Z"/>.
		/// </returns>
		public static Vector3 ToVector3(ref Vector2 source)
		{
			return new Vector3(source.X, 0.0f, source.Y);
		}

		/// <summary>
		/// Converts the source <see cref="Vector2"/> to a <see cref="Vector3"/> with a zero y-component.
		/// </summary>
		/// <param name="source">The source vector.</param>
		/// <param name="result">
		/// A <see cref="Vector3"/> where <see cref="result.X"/> equals <paramref name="source.X"/>
		/// and <see cref="result.Y"/> equals zero and <see cref="result.Z"/> equals <paramref name="source.Z"/>.
		/// </param>
		public static void ToVector3(ref Vector2 source, out Vector3 result)
		{
			result = new Vector3(source.X, 0.0f, source.Y);
		}
    }
}
